/**
 * @file    MessageProcessingCommon.h
 * @brief   Collects some common message-processing methods, used by bundles.
 *
 ****************************************************************************
 * @version $Id: MessageProcessingCommon.h 2574 2010-05-27 09:04:30Z alex $
 * @author  Alessandro Polo
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

#ifndef __WOSH_Core_MessageProcessingCommon_H__
 #define __WOSH_Core_MessageProcessingCommon_H__

 #include <woshDefs.h>
 #include <core/Message.h>
 #include <core/Request.h>
 #include <core/Response.h>

 #include <core/Bus.h>
 #include <core/Thread.h>
 #include <core/MethodsCommon.h>
 #include <core/MessageFilter.h>
 #include <core/LogContext.h>
 #include <core/PropertiesProvider.h>
 #include <core/MethodsProvider.h>
 #include <core/InterfacesProvider.h>
 #include <core/BundleRequirements.h>
 #include <core/SecurityManager.h>


namespace wosh {

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

Message* MPC_busMessageRequest( const Message& requestMessage, MethodsProvider& Methods, LogContext& Log );

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool MPC_PropertyParse_LogLevel( const Variant& value_proposed, LogContext& Log );

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <class OBJECT_YPE>
Message* MPC_mmDo_ForSimpleObject( const Message& message, OBJECT_YPE* object, LogContext& Log ) {
	bool allowRead = SecurityManager::evalAuthorization( message.getSecurityToken(), &object->getPermission(), Permission::Read );
	if ( !allowRead ) {
		Log(LOG_WARNING, ":busMessageConnection() : Not allowed to read Host '%s' ", object->getName().c_str() );
		return NULL;
	 }
	const Request* request = message.getContent()->asRequest();
	Response* response = NULL;

	if ( request->getMethod() == _METHOD_GetProperty )
		response = MPC_mmDoPropertyGet( &message, object->getProperties(), Log );
	else if ( request->getMethod() == _METHOD_SetProperty )
		response = MPC_mmDoPropertySet( &message, object->getProperties(), Log );
	else if ( request->getMethod() == _METHOD_SetProperties )
		response = MPC_mmDoPropertiesSet( &message, object->getProperties(), Log );
	else if ( request->getMethod() == _METHOD_GetProperties )
		response = MPC_mmDoPropertiesGet( &message, object->getProperties(), Log );
	else if ( request->getMethod() == _METHOD_ListProperties )
		response = MPC_mmDoPropertyList( &message, object->getProperties(), Log );

	else if ( request->getMethod() == _METHOD_ChMod )
		response = MPC_mmDoChMod( &message, object, &object->getProperties(), NULL, &Log );
	else if ( request->getMethod() == _METHOD_ChOwn )
		response = MPC_mmDoChOwn( &message, object, &object->getProperties(), NULL, &Log );


	if ( response == NULL ) return NULL;
	Message* reply = message.replyTo(response);
	reply->setSource( object );
	return reply;
 }

template <class OBJECT_TYPE, typename KEY_TYPE>
void MPC_mmDo_SimpleObjectInContainer( const Message& message, MapT<KEY_TYPE, OBJECT_TYPE*>& container, LogContext& Log, BusConnector& cBus ) {
	typename MapT<KEY_TYPE, OBJECT_TYPE*>::Iterator it;
	typename MapT<KEY_TYPE, OBJECT_TYPE*>::ConstIterator it_end = container.end();
	for ( it=container.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( !URI::pregMatchStep(message.getDestination().getName(), it->second->getURI().getName() ) ) continue;
		if ( message.getDestination().hasExpression() ) {
			bool valid = MessageFilter::validateExpression( message.getDestination().getExpression(), &it->second->getProperties(), NULL, message.getSecurityToken() );
			if ( !valid ) continue;
		 }
		Message* reply = MPC_mmDo_ForSimpleObject<OBJECT_TYPE>( message, it->second, Log );
		if ( reply != NULL )
			cBus.postMessage(reply);
	 }
	return;
 }

template <class OBJECT_TYPE, typename KEY_TYPE>
void MPC_mmDo_InContainer( const Message& message, MapT<KEY_TYPE, OBJECT_TYPE*>& container, LogContext& Log, BusConnector& cBus ) {
	typename MapT<KEY_TYPE, OBJECT_TYPE*>::Iterator it;
	typename MapT<KEY_TYPE, OBJECT_TYPE*>::ConstIterator it_end = container.end();
	for ( it=container.begin(); it!=it_end; ++it ) {
		if ( it->second == NULL ) continue;
		if ( !URI::pregMatchStep(message.getDestination().getName(), it->second->getURI().getName() ) ) continue;
		if ( message.getDestination().hasExpression() ) {
			bool valid = MessageFilter::validateExpression( message.getDestination().getExpression(), &it->second->getProperties(), NULL, message.getSecurityToken() );
			if ( !valid ) continue;
		 }
		Message* reply = MPC_busMessageRequest( message, it->second->getMethods(), Log );
		if ( reply != NULL )
			cBus.postMessage(reply);
	 }
	return;
 }

template <class OBJECT_TYPE>
void MPC_mmDo_InContainer( const Message& message, ListT<OBJECT_TYPE*>& container, LogContext& Log, BusConnector& cBus ) {
	typename ListT<OBJECT_TYPE*>::Iterator it;
	typename ListT<OBJECT_TYPE*>::ConstIterator it_end = container.end();
	for ( it=container.begin(); it!=it_end; ++it ) {
		if ( *it == NULL ) continue;
		if ( !URI::pregMatchStep(message.getDestination().getName(), (*it)->getURI().getName() ) ) continue;
		if ( message.getDestination().hasExpression() ) {
			bool valid = MessageFilter::validateExpression( message.getDestination().getExpression(), &(*it)->getProperties(), NULL, message.getSecurityToken() );
			if ( !valid ) continue;
		 }
		Message* reply = MPC_busMessageRequest( message,(*it)->getMethods(), Log );
		if ( reply != NULL )
			cBus.postMessage(reply);
	 }
	return;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

class MessageReplyWaiter : public virtual IBusListener
 {

	public:
		MessageReplyWaiter( Message* requestMessage ) : IBusListener() {
			this->found_message = NULL;
			this->id = requestMessage->getID();
		 }
		virtual ~MessageReplyWaiter() { }

	public:
		virtual bool wait( long msec ) {
			long i = 0;
			while( ++i < msec ) {
				if ( this->found_message != NULL )
					return true;
				ThreadImpl::sleepForMSec(1);
			 }
			return false;
		 }

		virtual void busMessage( const Message& message, const Bus* ) {
			if ( message.getReferenceID() == this->id  )
				this->found_message = message.clone();
		 }

	public:
		int64 id;
		Message* found_message;

}; // class def


}; // namespace wosh

#endif //__WOSH_Core_MessageProcessingCommon_H__
